package io.lvdaxian.mybatis19.type;

import cn.hutool.core.annotation.Alias;
import io.lvdaxian.mybatis19.io.ResolverUtil;
import io.lvdaxian.mybatis19.io.Resources;

import java.util.*;

/**
 * 这是 类型别名的注册器
 *
 * @author lihh
 */
public class TypeAliasRegistry {
  private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<>();
  
  public TypeAliasRegistry() {
    registerAlias("string", String.class);
    
    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);
  }
  
  // 用来判断被加载过的资源
  private final Set<Object> loadedResources = new HashSet<>();
  
  /**
   * 注册 别名的方法
   *
   * @param alias 别名
   * @param value 以及别名对应的 Class
   * @author lihh
   */
  public void registerAlias(String alias, Class<?> value) {
    String key = alias.toLowerCase(Locale.ENGLISH);
    TYPE_ALIASES.put(key, value);
    TYPE_ALIASES.put(alias, value);
  }
  
  /**
   * 这是以类Class 进行注册
   *
   * @param type 类Class
   * @author lihh
   */
  public void registerAlias(Class<?> type) {
    // 类Class 的短名称
    String alias = type.getSimpleName();
    
    // 这里针对注解别名进行处理
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (null != aliasAnnotation)
      alias = aliasAnnotation.value();
    
    registerAlias(alias, type);
  }
  
  /**
   * 这里是 以包的形式注册
   *
   * @param packageName 包名
   * @author lihh
   */
  public void registerAliases(String packageName) {
    if (loadedResources.contains(packageName))
      return;
    
    registerAliases(packageName, Object.class);
  }
  
  /**
   * 这是批量注册 别名的逻辑
   *
   * @param packageName 包名
   * @param superType   父类包类型
   * @author lihh
   */
  private void registerAliases(String packageName, Class<?> superType) {
    // 判断是否被加载过
    if (loadedResources.contains(packageName))
      return;
    
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    
    for (Class<?> type : typeSet) {
      // Ignore inner classes and interfaces (including package-info.java)
      // Skip also inner classes. See issue #6
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        // 通过 Class 注册别名
        registerAlias(type);
        
        // 这里为了以防万一，类路径名称也注册上
        registerAlias(type.getName(), type);
      }
    }
    
    // 添加资源加载缓存
    loadedResources.add(packageName);
  }
  
  
  /**
   * 解析 别名
   *
   * @param alias 别名
   * @param <T>   泛型类型
   * @return 返回别名对应的类
   * @author lihh
   */
  public <T> Class<T> resolveAlias(String alias) {
    if (null == alias) return null;
    
    Class<T> value;
    String key = alias.toLowerCase(Locale.ENGLISH);
    if (TYPE_ALIASES.containsKey(key)) {
      value = (Class<T>) TYPE_ALIASES.get(key);
    } else {
      try {
        value = Resources.classForName(alias);
      } catch (Exception e) {
        return null;
      }
    }
    
    return value;
  }
}
